class Vue{
    constructor(options){
        this.$el = options.el;
        this.$data = options.data;

        if(this.$el){
            //数据拦截
            new Observer(this.$data);
            //模板编译
            new Complie(this.$el,this);
        }
    }
}

class Dep{
    constructor(){
        this.watchers = [];
    }

    add(watcher){
        this.watchers.push(watcher);
    }

    notify(){
        this.watchers.forEach(watcher => {
            watcher.update();
        })
    }
}

class Watcher{
    constructor(vm,expr,cb){
        this.vm = vm;
        this.expr = expr;
        this.cb = cb;
        this.oldValue = this.getVal();
    }

    getVal(){
        Dep.target = this;
        let value = compileUtil.getVal(this.expr,this.vm);
        Dep.target = null;
        return value;
    }

    update(){
        let newVal = compileUtil.getVal(this.expr,this.vm);
        if(newVal != this.oldValue){
            this.cb(newVal);
        }
    }
}

//vm.$watcher(vm,'user.username',(newval) => {})

class Observer{
    constructor(data){
        this.observer(data);
    }

    observer(data){
        if(data && typeof data === 'object'){
            for(let key in data){
                this.observerData(data,key,data[key]);
            }
        }
    }

    observerData(data,key,value){
        this.observer(value);
        let dep = new Dep();
        Object.defineProperty(data,key,{
            set(newVal){
                if(newVal != value){
                    value = newVal;
                    dep.notify();
                }
            },
            get(){
                Dep.target && dep.add(Dep.target)
                return value
            }
        })
    }
}





class Complie{
    constructor(el,vm){
        console.log(el)
        this.vm = vm;
        this.el = this.isElementNode(el) ? el : document.querySelector(el);

        console.log(this.el)
        //把html节点放到内存中
        let fragment = this.nodeToFragment(this.el);
        
        this.compile(fragment);

        this.el.appendChild(fragment);

    }

    //编译元素
    compileElement(node){
        //获取元素节点的所有属性
        let attributes = node.attributes;
        [...attributes].forEach(attr => {
            console.dir(attr);
            //name 是属性名 v-model  value 是属性值 user.username
            let {name,value:expr} = attr;
            if(this.isDirective(name)){
                //属性是指令  v-model
                let [,directive] = name.split('-');
                compileUtil[directive](node,expr,this.vm)
            }
        })
    }

    isDirective(attr){
        return attr.startsWith('v-');
    }

    //编译文本
    compileText(node){
        //获取文本的内容
        let content = node.textContent;
        //判断是否含有{{}}
        if(/\{\{(.+?)\}\}/.test(content)){
            compileUtil.content(node,content,this.vm);
        }
    }

    //编译
    compile(fragment){
        let childNodes = fragment.childNodes;
        // console.log(childNodes)
        [...childNodes].forEach(child => {
            if(this.isElementNode(child)){
                //是元素
                this.compileElement(child);
                this.compile(child);
            }else{
                //是文本
                this.compileText(child);
            }
        })
    }

    nodeToFragment(node){
        console.log(node)
        //创建文档片段，它不在主dom树内，在内存里，
        //所以将子元素插入到文档片段内，不会引起页面的回流
        let fragment = document.createDocumentFragment();

        let firstChild;

        while(firstChild = node.firstChild){
            fragment.appendChild(firstChild);
        }

        return fragment
    }

    isElementNode(node){
        //nodeType 返回节点类型  1.元素节点  2.属性节点  3.文本节点 8：注释节点
        return node.nodeType === 1
    }
}

compileUtil = {
    getVal(expr,vm){
        console.log(expr)
        return expr.split('.').reduce((pre,cur) => {
            return pre[cur];
        },vm.$data)
    },
    setVal(vm,expr,data){
        expr.split('.').reduce((pre,cur,index,arr) => {
            if(index === arr.length-1){
                pre[cur] = data
            }
            return pre[cur] 
        },vm.$data)
    },
    model(node,expr,vm){
        let val = this.getVal(expr,vm);
        let fn = this.updater.compileModel;
        //观察者
        new Watcher(vm,expr,(newValue) => {
            fn(node,newValue);
        })
        fn(node,val);

        node.addEventListener('input',(e) => {
            let data = e.target.value;
            this.setVal(vm,expr,data);
        })
    },
    content(node,content,vm){
        let value = content.replace(/\{\{(.+?)\}\}/g,(...argv) => {
            console.log(argv)
            //观察者
            new Watcher(vm,argv[1],(newValue) => {
                fn(node,newValue);
            })
            return this.getVal(argv[1],vm)
        })
        let fn = this.updater.compileContent;

        fn(node,value);
    },
    text(){

    },
    updater:{
        compileModel(node,value){
            node.value = value;
        },
        compileContent(node,value){
            node.textContent = value
        }
    }
}